package com.biscuit.page.basicservice.bean;

import com.biscuit.page.basicservice.conf.config.MinioClientExtend;
import com.biscuit.page.basicservice.conf.properties.MinioProperties;
import com.biscuit.page.basicservice.utils.DateUtil;
import com.google.common.collect.HashMultimap;
import io.minio.*;
import io.minio.messages.ListPartsResult;
import io.minio.messages.Part;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * @author biscuit
 * @createTime 2024/7/2 20:11
 */
@Slf4j
@Component
public class FileMinioClient {

    @Resource
    private MinioClientExtend client;

    @Resource
    private MinioProperties properties;

    public MinioAsyncClient getParentClient() {
        return this.client;
    }

    public String upload(String fileName, String prefixPath, InputStream file, String contentType) {
        try {
            String objectPath = (prefixPath.lastIndexOf("/") == prefixPath.length() - 1 ? prefixPath.substring(prefixPath.length() - 1) : prefixPath)
                    + "/" + DateUtil.format(new Date(), "yyyy/MMdd") + "/" + (fileName.indexOf("/") == 0 ? fileName.substring(1) : fileName);
            PutObjectArgs args = PutObjectArgs.builder()
                    .bucket(this.properties.getBucketName())
                    .object(objectPath)
                    .contentType(contentType)
                    .stream(file, file.available(), -1)
                    .build();
            ObjectWriteResponse response = this.client.putObject(args).get();
            return this.properties.getEndpoint() + "/" + this.properties.getBucketName() + objectPath;
        } catch (Exception e) {
            log.error("FileMinioClient upload 文件上传失败，message -> {}", e.getMessage());
        }
        return null;
    }

    // 文件分片上传 - 初始化
    public String initPartUpload(String objectName, String contentType) {
        try {
            // 文件类型为空初始化默认类型
            if (StringUtils.isBlank(contentType)) {
                contentType = "application/octet-stream";
            }

            // 文件分片上传请求头
            HashMultimap<String, String> headers = HashMultimap.create();
            headers.put("Content-Type", contentType);

            // 请求 minio 开启一个文件分片上传会话
            CompletableFuture<CreateMultipartUploadResponse> responseCompletableFuture = this.client.initMultipartUpload(
                    this.properties.getBucketName(),
                    null,
                    objectName,
                    headers,
                    null
            );

            // 等待异步执行完成，获取实际请求接口
            CreateMultipartUploadResponse response = responseCompletableFuture.get();

            // 返回文件分片上传会话Id
            return response.result().uploadId();
        } catch (Exception e) {
            log.error("FileMinioClient initMultiPartUpload error，message -> {}", e.getMessage());
        }
        return null;
    }

    public void uploadPart(int partIndex, String uploadId, String objectName, InputStream inputStream) {
        try {
            // 请求 minio 上传文件分片
            CompletableFuture<UploadPartResponse> responseCompletableFuture = this.client.uploadMultipart(
                    this.properties.getBucketName(),
                    null,
                    objectName,
                    new BufferedInputStream(inputStream),
                    inputStream.available(), // 不能使用 .getBytes() 获取文件大小，无法获取到
                    uploadId,
                    partIndex,
                    null,
                    null
            );

            // 获取实际上传结果
            UploadPartResponse uploadPartResponse = responseCompletableFuture.get();
        } catch (Exception e) {
            log.error("FileMinioClient uploadPart error，message -> {}", e.getMessage());
        }
    }

    public ListPartsResult listPart(String uploadId, String objectName) {
        try {
            // 向 minio 请求文件分片信息
            CompletableFuture<ListPartsResponse> responseCompletableFuture = this.client.listMultipart(
                    this.properties.getBucketName(),
                    null,
                    objectName,
                    1000,
                    0,
                    uploadId,
                    null,
                    null
            );

            // 获取文件分片信息实际相应信息
            ListPartsResponse listPartsResponse = responseCompletableFuture.get();

            // 返回文件分片信息
            return listPartsResponse.result();
        } catch (Exception e) {
            log.error("FileMinioClient listPart error，message -> {}", e.getMessage());
        }
        return null;
    }

    public String completePart(String uploadId, String objectName) {
        try {
            // 根据分片上传会话id，请求文件分片信息
            List<Part> partList = this.listPart(uploadId, objectName).partList();

            // 入参需要一个分片数组，将其转换为数组
            Part[] parts = new Part[partList.size()];
            for (int i = 0; i < partList.size(); i++) {
                parts[i] = new Part(partList.get(i).partNumber(), partList.get(i).etag());
            }

            // 请求 minio 合并文件分片为完整文件
            CompletableFuture<ObjectWriteResponse> responseCompletableFuture = this.client.mergeMultipartUpload(
                    this.properties.getBucketName(),
                    null,
                    objectName,
                    uploadId,
                    parts,
                    null,
                    null
            );

            // 获取文件分片合并实际响应结果
            ObjectWriteResponse response = responseCompletableFuture.get();

            // 返回文件访问地址
            return this.properties.getEndpoint() + "/" + this.properties.getBucketName() + "/" + objectName;
        } catch (Exception e) {
            log.error("FileMinioClient completePart error，message -> {}", e.getMessage());
        }
        return null;
    }

}
